home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / chfn.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  12.4 KB  |  578 lines

  1. /*
  2.  * Copyright 1989, 1990, 1991, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  */
  11.  
  12. #include <sys/types.h>
  13. #include <stdio.h>
  14. #include <fcntl.h>
  15. #include <signal.h>
  16.  
  17. #ifndef    lint
  18. static    char    sccsid[] = "@(#)chfn.c    3.8    11:59:27    12/28/91";
  19. #endif
  20.  
  21. /*
  22.  * Set up some BSD defines so that all the BSD ifdef's are
  23.  * kept right here 
  24.  */
  25.  
  26. #ifndef    BSD
  27. #include <string.h>
  28. #include <memory.h>
  29. #else
  30. #include <strings.h>
  31. #define    strchr    index
  32. #define    strrchr    rindex
  33. #endif
  34.  
  35. #include "config.h"
  36. #include "pwd.h"
  37.  
  38. #ifdef    USE_SYSLOG
  39. #include <syslog.h>
  40.  
  41. #ifndef    LOG_WARN
  42. #define    LOG_WARN LOG_WARNING
  43. #endif
  44. #endif
  45. #ifdef    USE_RLIMIT
  46. #include <sys/resource.h>
  47.  
  48. struct    rlimit    rlimit_fsize = { RLIM_INFINITY, RLIM_INFINIT };
  49. #endif
  50.  
  51. /*
  52.  * Global variables.
  53.  */
  54.  
  55. char    *Progname;
  56. char    user[BUFSIZ];
  57. char    fullnm[BUFSIZ];
  58. char    roomno[BUFSIZ];
  59. char    workph[BUFSIZ];
  60. char    homeph[BUFSIZ];
  61. char    slop[BUFSIZ];
  62. int    amroot;
  63.  
  64. /*
  65.  * External identifiers
  66.  */
  67.  
  68. extern    int    optind;
  69. extern    char    *optarg;
  70. extern    struct    passwd    *getpwuid ();
  71. extern    struct    passwd    *getpwnam ();
  72. extern    char    *getlogin ();
  73. #ifdef    NDBM
  74. extern    int    pw_dbm_mode;
  75. #endif
  76.  
  77. /*
  78.  * #defines for messages.  This facilities foreign language conversion
  79.  * since all messages are defined right here.
  80.  */
  81.  
  82. #define    USAGE \
  83. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"
  84. #define    ADMUSAGE \
  85. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
  86.        [ -h home_ph ] [ -o other ] [ user ]\n"
  87. #define    NOPERM        "%s: Permission denied.\n"
  88. #define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  89. #define    INVALID_NAME    "%s: invalid name: \"%s\"\n"
  90. #define    INVALID_ROOM    "%s: invalid room number: \"%s\"\n"
  91. #define    INVALID_WORKPH    "%s: invalid work phone: \"%s\"\n"
  92. #define    INVALID_HOMEPH    "%s: invalid home phone: \"%s\"\n"
  93. #define    INVALID_OTHER    "%s: \"%s\" contains illegal characters\n"
  94. #define    INVALID_FIELDS    "%s: fields too long\n"
  95. #define    NEWFIELDSMSG    "Changing the user information for %s\n"
  96. #define    NEWFIELDSMSG2 \
  97. "Enter the new value, or press return for the default\n\n"
  98. #define    NEWNAME        "Full Name"
  99. #define    NEWROOM        "Room Number"
  100. #define    NEWWORKPHONE    "Work Phone"
  101. #define    NEWHOMEPHONE    "Home Phone"
  102. #define    NEWSLOP        "Other"
  103. #define    UNKUSER        "%s: Unknown user %s\n"
  104. #define    PWDBUSY        "Cannot lock the password file; try again later.\n"
  105. #define    PWDBUSY2    "can't lock /etc/passwd\n"
  106. #define    OPNERROR    "Cannot open the password file.\n"
  107. #define    OPNERROR2    "can't open /etc/passwd\n"
  108. #define    UPDERROR    "Error updating the password entry.\n"
  109. #define    UPDERROR2    "error updating passwd entry\n"
  110. #define    DBMERROR    "Error updating the DBM password entry.\n"
  111. #define    DBMERROR2    "error updating DBM passwd entry.\n"
  112. #define    NOTROOT        "Cannot change ID to root.\n"
  113. #define    NOTROOT2    "can't setuid(0).\n"
  114. #define    CLSERROR    "Cannot commit password file changes.\n"
  115. #define    CLSERROR2    "can't rewrite /etc/passwd.\n"
  116. #define    UNLKERROR    "Cannot unlock the password file.\n"
  117. #define    UNLKERROR2    "can't unlock /etc/passwd.\n"
  118. #define    CHGGECOS    "changed user `%s' information.\n"
  119.  
  120. /*
  121.  * usage - print command line syntax and exit
  122.  */
  123.  
  124. void
  125. usage ()
  126. {
  127.     fprintf (stderr, amroot ? USAGE:ADMUSAGE, Progname);
  128.     exit (1);
  129. }
  130.  
  131. /*
  132.  * new_fields - change the user's GECOS information interactively
  133.  *
  134.  * prompt the user for each of the four fields and fill in the fields
  135.  * from the user's response, or leave alone if nothing was entered.
  136.  */
  137.  
  138. new_fields ()
  139. {
  140.     printf (NEWFIELDSMSG2);
  141.  
  142.     change_field (fullnm, NEWNAME);
  143.     change_field (roomno, NEWROOM);
  144.     change_field (workph, NEWWORKPHONE);
  145.     change_field (homeph, NEWHOMEPHONE);
  146.  
  147.     if (amroot)
  148.         change_field (slop, NEWSLOP);
  149. }
  150.  
  151. /*
  152.  * copy_field - get the next field from the gecos field
  153.  *
  154.  * copy_field copies the next field from the gecos field, returning a
  155.  * pointer to the field which follows, or NULL if there are no more
  156.  * fields.
  157.  */
  158.  
  159. char *
  160. copy_field (in, out, extra)
  161. char    *in;            /* the current GECOS field */
  162. char    *out;            /* where to copy the field to */
  163. char    *extra;            /* fields with '=' get copied here */
  164. {
  165.     char    *cp;
  166.  
  167.     while (in) {
  168.         if (cp = strchr (in, ','))
  169.             *cp++ = '\0';
  170.  
  171.         if (! strchr (in, '='))
  172.             break;
  173.  
  174.         if (extra) {
  175.             if (extra[0])
  176.                 strcat (extra, ",");
  177.  
  178.             strcat (extra, in);
  179.         }
  180.         in = cp;
  181.     }
  182.     if (in && out)
  183.         strcpy (out, in);
  184.  
  185.     return cp;
  186. }
  187.  
  188. /*
  189.  * chfn - change a user's password file information
  190.  *
  191.  *    This command controls the GECOS field information in the
  192.  *    password file entry.
  193.  *
  194.  *    The valid options are
  195.  *
  196.  *    -f    full name
  197.  *    -r    room number
  198.  *    -w    work phone number
  199.  *    -h    home phone number
  200.  *    -o    other information (*)
  201.  *
  202.  *    (*) requires root permission to execute.
  203.  */
  204.  
  205. int
  206. main (argc, argv)
  207. int    argc;
  208. char    **argv;
  209. {
  210.     char    *cp;            /* temporary character pointer       */
  211.     struct    passwd    *pw;        /* password file entry               */
  212.     struct    passwd    pwent;        /* modified password file entry      */
  213.     char    old_gecos[BUFSIZ];    /* buffer for old GECOS fields       */
  214.     char    new_gecos[BUFSIZ];    /* buffer for new GECOS fields       */
  215.     int    flag;            /* flag currently being processed    */
  216.     int    fflg = 0;        /* -f - set full name                */
  217.     int    rflg = 0;        /* -r - set room number              */
  218.     int    wflg = 0;        /* -w - set work phone number        */
  219.     int    hflg = 0;        /* -h - set home phone number        */
  220.     int    oflg = 0;        /* -o - set other information        */
  221.     int    i;            /* loop control variable             */
  222.  
  223.     /*
  224.      * This command behaves different for root and non-root
  225.      * users.
  226.      */
  227.  
  228.     amroot = getuid () == 0;
  229. #ifdef    NDBM
  230.     pw_dbm_mode = O_RDWR;
  231. #endif
  232.  
  233.     /*
  234.      * Get the program name.  The program name is used as a
  235.      * prefix to most error messages.  It is also used as input
  236.      * to the openlog() function for error logging.
  237.      */
  238.  
  239.     if (Progname = strrchr (argv[0], '/'))
  240.         Progname++;
  241.     else
  242.         Progname = argv[0];
  243.  
  244. #ifdef    USE_SYSLOG
  245.     openlog (Progname, LOG_PID, LOG_AUTH);
  246. #endif
  247.  
  248.     /* 
  249.      * The remaining arguments will be processed one by one and
  250.      * executed by this command.  The name is the last argument
  251.      * if it does not begin with a "-", otherwise the name is
  252.      * determined from the environment and must agree with the
  253.      * real UID.  Also, the UID will be checked for any commands
  254.      * which are restricted to root only.
  255.      */
  256.  
  257.     while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
  258.         switch (flag) {
  259.             case 'f':
  260.                 fflg++;
  261.                 strcpy (fullnm, optarg);
  262.                 break;
  263.             case 'r':
  264.                 rflg++;
  265.                 strcpy (roomno, optarg);
  266.                 break;
  267.             case 'w':
  268.                 wflg++;
  269.                 strcpy (workph, optarg);
  270.                 break;
  271.             case 'h':
  272.                 hflg++;
  273.                 strcpy (homeph, optarg);
  274.                 break;
  275.             case 'o':
  276.                 if (amroot) {
  277.                     oflg++;
  278.                     strcpy (slop, optarg);
  279.                     break;
  280.                 }
  281.                 fprintf (stderr, NOPERM, Progname);
  282. #ifdef    USE_SYSLOG
  283.                 closelog ();
  284. #endif
  285.                 exit (1);
  286.             default:
  287.                 usage ();
  288.         }
  289.     }
  290.  
  291.     /*
  292.      * Get the name of the user to check.  It is either
  293.      * the command line name, or the name getlogin()
  294.      * returns.
  295.      */
  296.  
  297.     if (optind < argc) {
  298.         strncpy (user, argv[optind], sizeof user);
  299.         pw = getpwnam (user);
  300.     } else if (cp = getlogin ()) {
  301.         strncpy (user, cp, sizeof user);
  302.         pw = getpwnam (user);
  303.     } else {
  304.         fprintf (stderr, WHOAREYOU, Progname);
  305. #ifdef    USE_SYSLOG
  306.         closelog ();
  307. #endif
  308.         exit (1);
  309.     }
  310.  
  311.     /*
  312.      * Make certain there was a password entry for the
  313.      * user.
  314.      */
  315.  
  316.     if (! pw) {
  317.         fprintf (stderr, UNKUSER, Progname, user);
  318. #ifdef    USE_SYSLOG
  319.         closelog ();
  320. #endif
  321.         exit (1);
  322.     }
  323.  
  324.     /*
  325.      * Non-privileged users are only allowed to change the
  326.      * shell if the UID of the user matches the current
  327.      * real UID.
  328.      */
  329.  
  330.     if (! amroot && pw->pw_uid != getuid ()) {
  331.         fprintf (stderr, NOPERM, Progname);
  332. #ifdef    USE_SYSLOG
  333.         closelog ();
  334. #endif
  335.         exit (1);
  336.     }
  337.  
  338.     /*
  339.      * Make a copy of the user's password file entry so it
  340.      * can be modified without worrying about it be modified
  341.      * elsewhere.
  342.      */
  343.  
  344.     pwent = *pw;
  345.     pwent.pw_name = strdup (pw->pw_name);
  346.     pwent.pw_passwd = strdup (pw->pw_passwd);
  347. #ifdef    ATT_AGE
  348.     pwent.pw_age = strdup (pw->pw_age);
  349. #endif
  350. #ifdef    ATT_COMMENT
  351.     pwent.pw_comment = strdup (pw->pw_comment);
  352. #endif
  353.     pwent.pw_dir = strdup (pw->pw_dir);
  354.     pwent.pw_shell = strdup (pw->pw_shell);
  355.  
  356.     /*
  357.      * Now get the full name.  It is the first comma separated field
  358.      * in the GECOS field.
  359.      */
  360.  
  361.     strcpy (old_gecos, pw->pw_gecos);
  362.     cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
  363.  
  364.     /*
  365.      * Now get the room number.  It is the next comma separated field,
  366.      * if there is indeed one.
  367.      */
  368.  
  369.     if (cp)
  370.         cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
  371.  
  372.     /*
  373.      * Now get the work phone number.  It is the third field.
  374.      */
  375.  
  376.     if (cp)
  377.         cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
  378.  
  379.     /*
  380.      * Now get the home phone number.  It is the fourth field.
  381.      */
  382.  
  383.     if (cp)
  384.         cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
  385.  
  386.     /*
  387.      * Anything left over is "slop".
  388.      */
  389.  
  390.     if (cp) {
  391.         if (slop[0])
  392.             strcat (slop, ",");
  393.  
  394.         strcat (slop, cp);
  395.     }
  396.  
  397.     /*
  398.      * If none of the fields were changed from the command line,
  399.      * let the user interactively change them.
  400.      */
  401.  
  402.     if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
  403.         printf (NEWFIELDSMSG, user);
  404.         new_fields ();
  405.     }
  406.  
  407.     /*
  408.      * Check all of the fields for valid information
  409.      */
  410.  
  411.     if (valid_field (fullnm, ":,=")) {
  412.         fprintf (stderr, INVALID_NAME, Progname, fullnm);
  413. #ifdef    USE_SYSLOG
  414.         closelog ();
  415. #endif
  416.         exit (1);
  417.     }
  418.     if (valid_field (roomno, ":,=")) {
  419.         fprintf (stderr, INVALID_ROOM, Progname, roomno);
  420. #ifdef    USE_SYSLOG
  421.         closelog ();
  422. #endif
  423.         exit (1);
  424.     }
  425.     if (valid_field (workph, ":,=")) {
  426.         fprintf (stderr, INVALID_WORKPH, Progname, workph);
  427. #ifdef    USE_SYSLOG
  428.         closelog ();
  429. #endif
  430.         exit (1);
  431.     }
  432.     if (valid_field (homeph, ":,=")) {
  433.         fprintf (stderr, INVALID_HOMEPH, Progname, homeph);
  434. #ifdef    USE_SYSLOG
  435.         closelog ();
  436. #endif
  437.         exit (1);
  438.     }
  439.     if (valid_field (slop, ":")) {
  440.         fprintf (stderr, INVALID_OTHER, Progname, slop);
  441. #ifdef    USE_SYSLOG
  442.         closelog ();
  443. #endif
  444.         exit (1);
  445.     }
  446.  
  447.     /*
  448.      * Build the new GECOS field by plastering all the pieces together,
  449.      * if they will fit ...
  450.      */
  451.  
  452.     if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
  453.             strlen (homeph) + strlen (slop) > 80) {
  454.         fprintf (stderr, INVALID_FIELDS, Progname);
  455. #ifdef    USE_SYSLOG
  456.         closelog ();
  457. #endif
  458.         exit (1);
  459.     }
  460.     sprintf (new_gecos, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
  461.     if (slop[0]) {
  462.         strcat (new_gecos, ",");
  463.         strcat (new_gecos, slop);
  464.     }
  465.     pwent.pw_gecos = new_gecos;
  466.     pw = &pwent;
  467.  
  468.     /*
  469.      * Before going any further, raise the ulimit to prevent
  470.      * colliding into a lowered ulimit, and set the real UID
  471.      * to root to protect against unexpected signals.  Any
  472.      * keyboard signals are set to be ignored.
  473.      */
  474.  
  475. #ifdef    HAVE_ULIMIT
  476.     ulimit (2, 30000);
  477. #endif
  478. #ifdef    HAVE_RLIMIT
  479.     setrlimit (RLIMIT_FSIZE, &rlimit_fsize);
  480. #endif
  481.     if (setuid (0)) {
  482.         fprintf (stderr, NOTROOT);
  483. #ifdef    USE_SYSLOG
  484.         syslog (LOG_ERR, NOTROOT2);
  485.         closelog ();
  486. #endif
  487.         exit (1);
  488.     }
  489.     signal (SIGHUP, SIG_IGN);
  490.     signal (SIGINT, SIG_IGN);
  491.     signal (SIGQUIT, SIG_IGN);
  492. #ifdef    SIGTSTP
  493.     signal (SIGTSTP, SIG_IGN);
  494. #endif
  495.  
  496.     /*
  497.      * The passwd entry is now ready to be committed back to
  498.      * the password file.  Get a lock on the file and open it.
  499.      */
  500.  
  501.     for (i = 0;i < 30;i++)
  502.         if (pw_lock ())
  503.             break;
  504.  
  505.     if (i == 30) {
  506.         fprintf (stderr, PWDBUSY);
  507. #ifdef    USE_SYSLOG
  508.         syslog (LOG_WARN, PWDBUSY2);
  509.         closelog ();
  510. #endif
  511.         exit (1);
  512.     }
  513.     if (! pw_open (O_RDWR)) {
  514.         fprintf (stderr, OPNERROR);
  515.         (void) pw_unlock ();
  516. #ifdef    USE_SYSLOG
  517.         syslog (LOG_ERR, OPNERROR2);
  518.         closelog ();
  519. #endif
  520.         exit (1);
  521.     }
  522.  
  523.     /*
  524.      * Update the passwd file entry.  If there is a DBM file,
  525.      * update that entry as well.
  526.      */
  527.  
  528.     if (! pw_update (pw)) {
  529.         fprintf (stderr, UPDERROR);
  530.         (void) pw_unlock ();
  531. #ifdef    USE_SYSLOG
  532.         syslog (LOG_ERR, UPDERROR2);
  533.         closelog ();
  534. #endif
  535.         exit (1);
  536.     }
  537. #if defined(DBM) || defined(NDBM)
  538.     if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
  539.         fprintf (stderr, DBMERROR);
  540.         (void) pw_unlock ();
  541. #ifdef    USE_SYSLOG
  542.         syslog (LOG_ERR, DBMERROR2);
  543.         closelog ();
  544. #endif
  545.         exit (1);
  546.     }
  547.     endpwent ();
  548. #endif
  549.  
  550.     /*
  551.      * Changes have all been made, so commit them and unlock the
  552.      * file.
  553.      */
  554.  
  555.     if (! pw_close ()) {
  556.         fprintf (stderr, CLSERROR);
  557.         (void) pw_unlock ();
  558. #ifdef    USE_SYSLOG
  559.         syslog (LOG_ERR, CLSERROR2);
  560.         closelog ();
  561. #endif
  562.         exit (1);
  563.     }
  564.     if (! pw_unlock ()) {
  565.         fprintf (stderr, UNLKERROR);
  566. #ifdef    USE_SYSLOG
  567.         syslog (LOG_ERR, UNLKERROR2);
  568.         closelog ();
  569. #endif
  570.         exit (1);
  571.     }
  572. #ifdef    USE_SYSLOG
  573.     syslog (LOG_INFO, CHGGECOS, user);
  574.     closelog ();
  575. #endif
  576.     exit (0);
  577. }
  578.